using System.Collections.Specialized;
using System.Web.Security;
using Be.Timvw.Framework.Security;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NMock2;
using OTPProvider = Be.Timvw.Framework.Web.Security.OneTimePasswordMembershipProvider;

namespace Be.Timvw.Framework.Web.Tests.Security.OneTimePasswordMembershipProvider
{
    [TestClass]
    public class WhenValidating
    {
        #region Infrastructure for all tests in this class

        private IMembershipProvider membershipProvider;
        private Mockery mocks;

        [TestInitialize]
        public void Initialize()
        {
            this.mocks = new Mockery();
            this.membershipProvider = (IMembershipProvider) this.mocks.NewMock(typeof(IMembershipProvider));
            MockMembershipProviderWrapper.MembershipProvider = this.membershipProvider;
        }

        [TestCleanup]
        public void Cleanup()
        {
            this.mocks.VerifyAllExpectationsHaveBeenMet();
            this.mocks = null;
        }

        public NameValueCollection GetConfig()
        {
            NameValueCollection config = new NameValueCollection();
            config.Add(OTPProvider.ProviderTypeKey, typeof(MockMembershipProviderWrapper).AssemblyQualifiedName);
            return config;
        }

        public OTPProvider CreateAndInitializeOneTimePasswordMembershipProvider()
        {
            string name = "/";
            return this.CreateAndInitializeOneTimePasswordMembershipProvider(name, this.GetConfig());
        }

        public OTPProvider CreateAndInitializeOneTimePasswordMembershipProvider(string name, NameValueCollection config)
        {
            string expectedName = name;
            NameValueCollection expectedConfig = config;
            Expect.Once.On(this.membershipProvider).Method("Initialize").With(expectedName, expectedConfig);

            OTPProvider otpMembershipProvider = new OTPProvider();
            otpMembershipProvider.Initialize(name, config);
            return otpMembershipProvider;
        }

        #endregion

        #region Tests for ValidateUser

        [TestMethod]
        public void ShouldReturnTrueWhenPasswordIsValid()
        {
            string name = "/";

            NameValueCollection config = new NameValueCollection();
            config.Add(OTPProvider.ProviderTypeKey, typeof(MockMembershipProviderWrapper).AssemblyQualifiedName);
            config.Add(OTPProvider.PasswordNumberOfDigitsKey, HmacOneTimePassword.DefaultNumberOfDigits.ToString());
            config.Add(OTPProvider.PasswordAlgorithmKey, HmacOneTimePassword.DefaultAlgorithmName);
            config.Add(OTPProvider.PasswordEpochKey, TimeOneTimePassword.DefaultEpoch.ToString());
            config.Add(OTPProvider.PasswordTimeStepKey, TimeOneTimePassword.DefaultTimeStepSeconds.ToString());

            OTPProvider otpMembershipProvider = this.CreateAndInitializeOneTimePasswordMembershipProvider(name, config);

            string username = "username";
            string secret = "secret";
            bool userIsOnline = false;
            MembershipUser membershipUser = new MockMembershipUser(username, secret);

            Expect.Once.On(this.membershipProvider).Method("GetUser").With(username, userIsOnline).Will(Return.Value(membershipUser));

            string password = new TimeOneTimePassword(secret).GetPassword();

            Assert.IsTrue(otpMembershipProvider.ValidateUser(username, password));
        }

        [TestMethod]
        public void ShouldReturnFalseWhenPasswordIsInvalid()
        {
            string name = "/";

            NameValueCollection config = new NameValueCollection();
            config.Add(OTPProvider.ProviderTypeKey, typeof(MockMembershipProviderWrapper).AssemblyQualifiedName);
            config.Add(OTPProvider.PasswordNumberOfDigitsKey, HmacOneTimePassword.DefaultNumberOfDigits.ToString());
            config.Add(OTPProvider.PasswordAlgorithmKey, HmacOneTimePassword.DefaultAlgorithmName);
            config.Add(OTPProvider.PasswordEpochKey, TimeOneTimePassword.DefaultEpoch.ToString());
            config.Add(OTPProvider.PasswordTimeStepKey, TimeOneTimePassword.DefaultTimeStepSeconds.ToString());

            OTPProvider otpMembershipProvider = this.CreateAndInitializeOneTimePasswordMembershipProvider(name, config);

            string username = "username";
            string secret = "secret";
            bool userIsOnline = false;
            MembershipUser membershipUser = new MockMembershipUser(username, secret);

            Expect.Once.On(this.membershipProvider).Method("GetUser").With(username, userIsOnline).Will(Return.Value(membershipUser));

            string password = "fault";
            Assert.IsFalse(otpMembershipProvider.ValidateUser(username, password));
        }

        [TestMethod]
        public void ShouldReturnFalseWhenInternalExceptionIsThrown()
        {
            string name = "/";

            NameValueCollection config = new NameValueCollection();
            config.Add(OTPProvider.ProviderTypeKey, typeof(MockMembershipProviderWrapper).AssemblyQualifiedName);
            config.Add(OTPProvider.PasswordNumberOfDigitsKey, HmacOneTimePassword.DefaultNumberOfDigits.ToString());
            config.Add(OTPProvider.PasswordAlgorithmKey, HmacOneTimePassword.DefaultAlgorithmName);
            config.Add(OTPProvider.PasswordEpochKey, TimeOneTimePassword.DefaultEpoch.ToString());
            config.Add(OTPProvider.PasswordTimeStepKey, TimeOneTimePassword.DefaultTimeStepSeconds.ToString());

            OTPProvider otpMembershipProvider = this.CreateAndInitializeOneTimePasswordMembershipProvider(name, config);

            string username = "username";
            bool userIsOnline = false;
            MembershipUser membershipUser = new MockMembershipUserGetPasswordException(username);

            Expect.Once.On(this.membershipProvider).Method("GetUser").With(username, userIsOnline).Will(Return.Value(membershipUser));

            string password = "fault";
            Assert.IsFalse(otpMembershipProvider.ValidateUser(username, password));
        }

        #endregion
    }
}